package ua.com.alus.resvac.crm.services.spring;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.NIOFSDirectory;
import org.apache.lucene.util.Version;
import ua.com.alus.resvac.crm.domen.entities.BaseEntity;
import ua.com.alus.resvac.crm.domen.entities.Resume;
import ua.com.alus.resvac.crm.domen.entities.ResumeAccountDetails;
import ua.com.alus.resvac.crm.domen.entities.Vacancy;
import ua.com.alus.resvac.crm.domen.entities.VacancyAccountDetails;
import ua.com.alus.resvac.crm.domen.prototype.DocType;
import ua.com.alus.resvac.crm.lucene.SynonymAnalyzer;
import ua.com.alus.resvac.crm.lucene.TestSynonymEngine;
import ua.com.alus.resvac.crm.util.search.Search;

import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

/**
 * Lucene service impl
 *
 * @author Usatov Alexey
 */
public class LuceneServiceImpl implements LuceneService {

    private String indexResumeDir;
    private String indexVacancyDir;

    public void setIndexResumeDir(String indexResumeDir) {
        this.indexResumeDir = indexResumeDir;
    }

    public void setIndexVacancyDir(String indexVacancyDir) {
        this.indexVacancyDir = indexVacancyDir;
    }

    private AccountService accountService;

    public void setAccountService(AccountService accountService) {
        this.accountService = accountService;
    }

    private static final String MAIN_INDEX = "PositionNameDescription";
    private static final String UUID_INDEX = "pkUuid";
    private static final String REGION_INDEX = "fkRegionId";
    private static final char SPACE = ' ';


    @Override
    public void indexRecord(BaseEntity baseEntity) throws IOException, ParseException {
        if (baseEntity instanceof Resume) {
            indexResume((Resume) baseEntity);
        } else if (baseEntity instanceof Vacancy) {
            indexVacancy((Vacancy) baseEntity);
        }

    }

    @Override
    public void deleteRecord(String index, DocType docType) throws IOException, ParseException {
        IndexWriter indexWriter;
        switch (docType) {
            case RESUME:
                indexWriter = getResumeIndexWriter();
                break;
            case VACANCY:
                indexWriter = getVacancyIndexWriter();
                break;
            default:
                indexWriter = getResumeIndexWriter();
                break;
        }
        try {
            indexWriter.deleteDocuments(new Term(UUID_INDEX, index));
        } finally {
            indexWriter.commit();
        }
    }

    private void indexResume(Resume resume)
            throws IOException, ParseException {
        ResumeAccountDetails accDetails = accountService.getResumeAccountDetails(resume.getAccount().getUuid());
        Document document = new Document();

        try {
            deleteRecord(resume.getUuid(), DocType.RESUME);
            StringBuilder mainIndexField = new StringBuilder();
            mainIndexField.append(resume.getPosition()).append(SPACE).append(accDetails.getFirstName())
                    .append(SPACE).append(accDetails.getLastName());
            document.add(new Field(UUID_INDEX, resume.getUuid(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.add(new Field(MAIN_INDEX, mainIndexField.toString(),
                    Field.Store.NO, Field.Index.ANALYZED));
            document.add(new Field(REGION_INDEX, String.valueOf(resume.getFkRegionId()),
                    Field.Store.YES, Field.Index.NOT_ANALYZED));
            getResumeIndexWriter().updateDocument(new Term(UUID_INDEX, resume.getUuid()), document);
        } finally {
            getResumeIndexWriter().commit();
        }
    }

    private void indexVacancy(Vacancy vacancy)
            throws IOException, ParseException {
        VacancyAccountDetails accDetails = accountService.getVacancyAccountDetails(vacancy.getAccount().getUuid());
        Document document = new Document();
        try {
            StringBuilder mainIndexField = new StringBuilder();
            mainIndexField.append(vacancy.getPosition()).append(SPACE).append(accDetails.getName())
                    .append(SPACE).append(accDetails.getDescription());
            document.add(new Field(UUID_INDEX, vacancy.getUuid(), Field.Store.YES, Field.Index.NOT_ANALYZED));
            document.add(new Field(MAIN_INDEX, mainIndexField.toString(),
                    Field.Store.NO, Field.Index.ANALYZED));
            document.add(new Field(REGION_INDEX, String.valueOf(vacancy.getFkRegionId()),
                    Field.Store.YES, Field.Index.NOT_ANALYZED));
            getVacancyIndexWriter().updateDocument(new Term(UUID_INDEX, vacancy.getUuid()), document);
        } finally {
            getVacancyIndexWriter().commit();
        }
    }

    private volatile IndexWriter resumeIndexWriter;

    private IndexWriter getResumeIndexWriter() throws IOException {
        if (resumeIndexWriter == null) {
            synchronized (this) {
                if (resumeIndexWriter == null) {
                    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, createAnalyzer());
                    indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
                    Directory directory = new NIOFSDirectory(new File(indexResumeDir));
                    resumeIndexWriter = new IndexWriter(directory, indexWriterConfig);
                }
            }
        }
        return resumeIndexWriter;
    }

    private volatile IndexWriter vacancyIndexWriter;

    private IndexWriter getVacancyIndexWriter() throws IOException {
        if (vacancyIndexWriter == null) {
            synchronized (this) {
                if (vacancyIndexWriter == null) {
                    IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, createAnalyzer());
                    indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
                    Directory directory = new NIOFSDirectory(new File(indexVacancyDir));
                    vacancyIndexWriter = new IndexWriter(directory, indexWriterConfig);
                }
            }
        }
        return vacancyIndexWriter;
    }

    @Override
    public List<String> search(Search search) throws IOException, ParseException {

        IndexSearcher indexSearcher = null;
        try {
            QueryParser queryParser = new QueryParser(Version.LUCENE_36, MAIN_INDEX, createAnalyzer());
            Query query = queryParser.parse(REGION_INDEX + ":" + search.getRegionId() + " AND " + search.getPosition());
            IndexReader reader;
            switch (search.getSearchType()) {
                case RESUME:
                    reader = IndexReader.open(new NIOFSDirectory(new File(indexResumeDir)));
                    break;
                case VACANCY:
                    reader = IndexReader.open(new NIOFSDirectory(new File(indexVacancyDir)));
                    break;
                default:
                    reader = IndexReader.open(new NIOFSDirectory(new File(indexResumeDir)));
                    break;
            }


            indexSearcher = new IndexSearcher(reader);
            TopDocs found = indexSearcher.search(query, 100);
            List<String> result = new LinkedList<String>();
            for (ScoreDoc scoreDoc : found.scoreDocs) {
                result.add(indexSearcher.doc(scoreDoc.doc).get(UUID_INDEX));
            }
            return result;
        } finally {
            if (indexSearcher != null) {
                indexSearcher.close();
            }
        }
    }

    private Analyzer createAnalyzer() {
        Analyzer analyzer;
        //analyzer = new StandardAnalyzer(Version.LUCENE_36);
        analyzer = new SynonymAnalyzer(new TestSynonymEngine());
        return analyzer;
    }
}
